////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_INTERNAL_X86_SSE_BASIC_INL__
#define __WN_MATH_INTERNAL_X86_SSE_BASIC_INL__

#ifndef __WN_MATH_BASIC_H__
    #error "x86/SSE/WNBasic.inl should never be included directly. Please include WNBasic.h instead"
#elif !defined _WN_X86
    #error "x86/SSE/WNBasic.inl has been included on a non x86 platform. Please rectify this."
#elif !defined __WN_MATH_ENABLE_EXTENSIONS
    #error "x86/SSE/WNBasic.inl has been included when extensions are disabled. Please rectify this."
#elif !defined __WN_MATH_SSE_ALLOWED
    #error "x86/SSE/WNBasic.inl has been included when SSE is not allowed. Please rectify this."
#endif

#include <xmmintrin.h>

namespace WNMath {
    namespace __WNInternal {
        __WN_MATH_SSE_INLINE WN_FLOAT32 __WNSqrtSSE(const WN_FLOAT32& _number) {
            #ifdef __WN_MATH_ENABLE_APPROXIMATIONS
                __m128 number = _mm_load_ss(&_number);
                __m128 compare = _mm_cmpneq_ss(_mm_setzero_ps(), number);
                __m128 rsqrt = _mm_rsqrt_ss(number);

                return(_mm_cvtss_f32(_mm_mul_ss(number, _mm_and_ps(compare, rsqrt))));
            #else
                return(_mm_cvtss_f32(_mm_sqrt_ss(_mm_load_ss(&_number))));
            #endif
        }

        __WN_MATH_SSE_INLINE WN_FLOAT32 __WNInvSqrtSSE(const WN_FLOAT32& _number) {
            #ifdef __WN_MATH_ENABLE_APPROXIMATIONS
                return(_mm_cvtss_f32(_mm_rsqrt_ss( _mm_load_ss(&_number))));
            #else
                __m128 sqrt = _mm_sqrt_ss( _mm_load_ss(&_number));
                __m128 one = _mm_set_ss(1.0f);

                return(_mm_cvtss_f32(_mm_div_ss(one, sqrt)));
            #endif
        }

        __WN_MATH_SSE_INLINE WN_FLOAT32 __WNMinSSE(const WN_FLOAT32& _first, const WN_FLOAT32& _second) {
            __m128 first = _mm_load_ss(&_first);
            __m128 second = _mm_load_ss(&_second);

            return(_mm_cvtss_f32(_mm_min_ss(first, second)));
        }

        __WN_MATH_SSE_INLINE WN_FLOAT32 __WNMaxSSE(const WN_FLOAT32& _first, const WN_FLOAT32& _second) {
            __m128 first = _mm_load_ss(&_first);
            __m128 second = _mm_load_ss(&_second);

            return(_mm_cvtss_f32(_mm_max_ss(first, second)));
        }

        __WN_MATH_SSE_INLINE WN_FLOAT32 __WNClampSSE(const WN_FLOAT32& _number, const WN_FLOAT32& _min, const WN_FLOAT32& _max) {
            __m128 number = _mm_load_ss(&_number);
            __m128 min = _mm_load_ss(&_min);
            __m128 max = _mm_load_ss(&_max);

            return(_mm_cvtss_f32(_mm_min_ss(_mm_max_ss(number, min), max)));
        }
    }
}

#endif // __WN_MATH_INTERNAL_X86_SSE_BASIC_INL__